package com.gjp.etfweb.utils;

import com.google.gson.Gson;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.util.*;

/**
 * @author 郭金鹏
 */
public class BeanConvertUtils {

    /**
     * 方法说明：对象转换
     * 将bean转化为另一种bean实体
     * 将一个对象转换为json再转回去，可以避免 BeanUtils 里的类型问题
     * 推荐 "字段类型" 不一样的对象转换，调用此方法
     *
     * @param object
     * @param targetClass
     * @param <T>
     * @return
     */
    public static <T> T convertBean(Object object, Class<T> targetClass, String... ignoreProperties) {
        if (ObjectUtils.isEmpty(object)) {
            return null;
        }
        //如果忽略配置为空，则使用转为 json 的方式，否则用 BeanUtils
        if (ignoreProperties == null || ignoreProperties.length == 0) {
            Gson gson = GsonUtils.GSON;
            return gson.fromJson(gson.toJson(object), targetClass);
        } else {
            return copy(object, targetClass, ignoreProperties);
        }
    }

    /**
     * 方法说明：对象转换(List)
     *
     * @param sourceList  原对象
     * @param targetClazz 目标对象
     * @return
     */
    public static <T, E> List<T> convertList(List<E> sourceList, Class<T> targetClazz, String... ignoreProperties) {
        List<T> targetList = new ArrayList<>();
        if (CollectionUtils.isEmpty(sourceList)) {
            return targetList;
        }
        for (E e : sourceList) {
            targetList.add(convertBean(e, targetClazz, ignoreProperties));
        }
        return targetList;
    }

    /**
     * 方法说明：对象转换
     *  需要忽略某些字段时，或者 entity - dto 层转换，调用这个方法
     *
     * @param source           原对象
     * @param target           目标对象
     * @param ignoreProperties 排除要copy的属性
     * @return
     */
    private static <T> T copy(Object source, Class<T> target, String... ignoreProperties) {
        T targetInstance = null;
        try {
            targetInstance = target.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (ignoreProperties != null) {
            BeanConvertUtils.copyPropertiesIgnoreEmpty(source, targetInstance);
        } else {
            BeanConvertUtils.copyPropertiesIgnoreEmpty(source, targetInstance, ignoreProperties);
        }
        return targetInstance;

    }

    /**
     * 方法说明：map转化为对象
     *
     * @param map
     * @param t
     * @param <T>
     * @return
     */
    public static <T> T mapToObject(Map<String, Object> map, Class<T> t) {
        return convertBean(map, t);
    }

    /**
     * 方法说明：对象转化为Map
     *  
     *
     * @param object
     * @return
     */
    public static Map<?, ?> objectToMap(Object object) {
        return convertBean(object, Map.class);
    }

    /**
     * 忽略为null或空字符串的属性
     *
     * @param source
     * @param target
     * @param property
     */
    public static void copyPropertiesIgnoreEmpty(Object source, Object target, String... property) {
        String[] s1 = getEmptyPropertyNames(source);
        String[] s2 = property;
        // 合并两个数组
        String[] s3 = new String[s1.length + s2.length];
        System.arraycopy(s1, 0, s3, 0, s1.length);
        System.arraycopy(s2, 0, s3, s1.length, s2.length);
        //去重
        HashSet<String> s4 = new HashSet(Arrays.asList(s3));
        BeanUtils.copyProperties(source, target, s4.toArray(new String[]{}));
    }

    /**
     * 获取属性为null或者空字符的属性名
     *
     * @param source 原始对象
     * @return 字段为 null 或空字串的数组
     */
    private static String[] getEmptyPropertyNames(Object source) {
        final BeanWrapper src = new BeanWrapperImpl(source);
        java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors();

        Set<String> emptyNames = new HashSet<>();
        for (java.beans.PropertyDescriptor pd : pds) {
            //如果是接口类，则跳过
            if (pd.getPropertyType().isInterface()) {
                continue;
            }

            Object srcValue = src.getPropertyValue(pd.getName());
            if (StringUtils.isEmpty(srcValue)) {
                emptyNames.add(pd.getName());
            }
        }
        String[] result = new String[emptyNames.size()];
        return emptyNames.toArray(result);
    }

}
